package com.tuo51.foudation.utils.export;

import com.starinside.wp.utils.DateUtil;
import com.tuo51.foudation.utils.ImgUtil;

import com.tuo51.foudation.utils.export.meta.StandardValues;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.PeriodType;

import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class HealthReportUtil {
    public static final String N3SD = "N3SD";
    public static final String N2SD = "N2SD";
    public static final String N1SD = "N1SD";
    public static final String SD = "SD";
    public static final String P1SD = "P1SD";
    public static final String P2SD = "P2SD";
    public static final String P3SD = "P3SD";

    public static final String[] SD_NAMES = {"-3SD","-2SD","-1SD","SD","+1SD","+2SD","+3SD"};

    public static final String RESULT_N3SD = "小于-3SD";
    public static final String RESULT_N2SD = "-3SD ~ -2SD";
    public static final String RESULT_N1SD = "-2SD ~ -1SD";
    public static final String RESULT_SD = "-1SD ~ +1SD";
    public static final String RESULT_P1SD = "+1SD ~ +2SD";
    public static final String RESULT_P2SD = "+2SD ~ +3SD";
    public static final String RESULT_P3SD = "大于+3SD";

    public static final String[] RESULT_SD_VALUES = {RESULT_N3SD, RESULT_N2SD, RESULT_N1SD, RESULT_SD, RESULT_P1SD, RESULT_P2SD, RESULT_P3SD};

    private static final String[] readable_heights = {
            "低于正常范围，现有身高水平比年龄平均值低 (极少儿童的发育水平处在这个区间，比例小于0.5%)。",
            "低于正常范围，现有身高水平比年龄平均值低（约4.6%儿童的发育水平处在这个区间）。",
            "稍低于正常范围，现有身高水平比年龄平均值略低（约27.4%儿童的发育水平处在这个区间）。",
            "属于正常范围，现有身高水平与年龄平均值相当（约68%儿童的发育水平处在这个区间）。",
            "稍高于正常范围，现有身高水平比年龄平均值略高（约27.4%儿童的发育水平处在这个区间）。",
            "高于正常范围，现有身高水平比年龄平均值高（约2.3%儿童的发育水平处在这个区间）。",
            "高于正常范围，现有身高水平比年龄平均值高（极少儿童的发育水平处在这个区间，比例小于0.5%）。"};

    private static final String[] readable_weights = {
            "低于正常范围，现有体重水平比年龄平均值低 (极少儿童的发育水平处在这个区间，比例小于0.5%)。",
            "低于正常范围，现有体重水平比年龄平均值低（约4.6%儿童的发育水平处在这个区间）。",
            "稍低于正常范围，现有体重水平比年龄平均值略低（约27.4%儿童的发育水平处在这个区间）。",
            "属于正常范围，现有体重水平与年龄平均值相当（约68%儿童的发育水平处在这个区间）。",
            "稍高于正常范围，现有体重水平比年龄平均值略高（约27.4%儿童的发育水平处在这个区间）。",
            "高于正常范围，现有体重水平比年龄平均值高（约4.6%儿童的发育水平处在这个区间）。",
            "高于正常范围，现有体重水平比年龄平均值高（极少儿童的发育水平处在这个区间，比例小于0.5%）。"};


    private static boolean compare(double value1, char operator, double value2){
        switch (operator){
            case '<':
                return value1 < value2;
            case '>':
                return value1 > value2;
            case 'G':
            case 'g':
                return value1 >= value2;
            case 'L':
            case 'l':
                return value1 <= value2;
            default:
                return value1 == value2;
        }
    }
    private static boolean judgeValueStandardValues (double xBase, String gender, String type, double value, String line, char symbol) {

        gender = gender.equals("女")?"girl":"boy";

        Map valueP = StandardValues.valuesMap();

        String xKeyTail = ("l2w".equals(type) || "h2w".equals(type))? "height" : "month";
        String xKey = String.format("%s_%s_%s", gender, type, xKeyTail) ;// boy_height_month

        int[] xValues = (int[]) valueP.get(xKey); //x轴值

        String line_key = gender + '_'+ type + "_" + line;
        double[] line_values = (double[])valueP.get(line_key);

        for ( int i = 0 ; i < xValues.length; i++) {
            if (xBase == xValues[i] || (xBase < xValues[i] && i == 0)) {
                double cv = line_values[i];
                return compare(value , symbol , cv);
            }

            if (xBase < xValues[i]) {
                double cv1 = line_values[i-1];
                double cv = line_values[i];

                //没有完全匹配的x值时，计算一个线性平均值
                double v = cal(xValues[i-1], cv1, xValues[i], cv, xBase);
                return compare(value , symbol , v);
            }
        }

        return false;
    }

    private static double cal(double start_m, double start_v, double end_m, double end_v, double target_m) {
        return start_v + (end_v - start_v) / (end_m - start_m) * (target_m - start_m);
    }

    public static String readableHeightPercent(String result){
        for(int i = 0; i < RESULT_SD_VALUES.length; i++){
            if(RESULT_SD_VALUES[i].equals(result)){
                return readable_heights[i];
            }
        }

        return result;
    }

    public static String readableWeightPercent(String result){
        for(int i = 0; i < RESULT_SD_VALUES.length; i++){
            if(RESULT_SD_VALUES[i].equals(result)){
                return readable_weights[i];
            }
        }

        return result;
    }

    public static int indexOfPercent(String percent){
        for(int i = 0 ; i < RESULT_SD_VALUES.length; i++){
            if(RESULT_SD_VALUES[i].equals(percent)){
                return i;
            }
        }

        return 0;
    }

    public static String calcPercent(double xBase, String gender, String type, double value){
        String heightPercent;
        if(judgeValueStandardValues(xBase, gender, type, value, N3SD, '<')){
            heightPercent = RESULT_N3SD;
        }else if(judgeValueStandardValues(xBase, gender, type, value, N2SD, '<')){
            heightPercent = RESULT_N2SD;
        }else if(judgeValueStandardValues(xBase, gender, type, value, N1SD, '<')){
            heightPercent = RESULT_N1SD;
        }else if(judgeValueStandardValues(xBase, gender, type, value, N1SD, 'g')  //>=
                && judgeValueStandardValues(xBase, gender, type, value, P1SD, 'l')){  // <=
            heightPercent = RESULT_SD;
        }else if(judgeValueStandardValues(xBase, gender, type, value, P3SD, 'g')){
            heightPercent = RESULT_P3SD;
        }else if(judgeValueStandardValues(xBase, gender, type, value, P2SD, 'g')){
            heightPercent = RESULT_P2SD;
        }else if(judgeValueStandardValues(xBase, gender, type, value, P1SD, 'g')){
            heightPercent = RESULT_P1SD;
        }else{
            //never been here
            heightPercent = "";
        }
        return heightPercent;
    }

    public static String calcWeightPercent(int month, String gender, double weight) {
        return calcPercent(month, gender, "weight", weight);
    }

    public static String calcHeightPercent(int month, String gender, double height) {
        return calcPercent(month, gender, "height", height);
    }

    public static String calcHeightToWeightPercent(int month, String gender, double height, double weight) {

        String type = "l2w";
        if (month > 36){
            type = "h2w";
        }

        return calcPercent(height, gender, type, weight);
    }

    public static String heightConclusionSimple(String name, String gender, double height, int month ){
        String conclusion = "参照《中国7岁以下儿童生长发育参照标准》，" + name + "宝宝";

        String result = calcHeightPercent(month, gender, height);
        String readable = readableHeightPercent(result);

        conclusion += "的身高发育水平在身高标准值表标准差范围为 " + result + "，";
        conclusion += readable;

        return conclusion;
    }

    public static String weightConclusionSimple(String name, String gender, double height, double weight, int month ){
        String conclusion = "";

        String result = calcWeightPercent(month, gender, weight);
        String readable = readableWeightPercent(result);


        String h2w_result = calcHeightToWeightPercent(month, gender, height, weight);
        String h2w_readable = readableWeightPercent(h2w_result);


        conclusion += "按年龄测体重的标准差范围为" + result + ",";
        if (!h2w_result.equals(result)){
            conclusion += readable;
        }

        conclusion += "按身高测体重的标准差范围为" + h2w_result  + "，";
        conclusion += h2w_readable;

        conclusion += "若您有任何疑问，可咨询老师或相关专业医生。";

        return conclusion;
    }

    public static String heightConclusion(String name, String gender, double height, int month ){

        String conclusion = "如年龄身高曲线图所示，" + name + "宝宝实测身高" +String.format("%.2f", height)+  "cm。"
                + heightConclusionSimple(name, gender, height, month);

        if (month < 36){
            conclusion = conclusion.replaceAll("身高", "身长");
        }

        return conclusion;
    }

    public static String weightConclusion(String name, String gender, double height, double weight, int month ){

        String conclusion = "如年龄体重曲线图所示，" + name + "宝宝实测体重" +String.format("%.2f", weight)+  "kg。"
                + weightConclusionSimple(name, gender, height, weight, month);

        if (month < 36){
            conclusion = conclusion.replaceAll("身高", "身长");
        }

        return conclusion;
    }


    public static String[] heightLineNames(String gender){
        String[] ret = {"P3", "P20", "P50", "P80", "P97"};
        return ret;
    }

    public static double[][] heightLineValues(String gender) {
        if (gender.equals("男")) {
            double[][] ret = {
                    {52.50, 56.40, 60.20, 62.10, 63.80, 65.50, 68.10, 70.10, 72.80, 76.50, 79.00, 81.50, 85.20, 89.30, 93.00, 96.40, 100.30, 103.60, 105.30, 108.40, 112.20},
                    {54.40, 58.50, 62.00, 64.10, 66.00, 67.70, 70.50, 73.00, 75.50, 79.50, 81.70, 85.00, 88.00, 93.30, 96.80, 100.00, 104.20, 107.20, 110.70, 113.20, 118.10},
                    {56.00, 60.30, 63.50, 66.00, 67.50, 69.50, 73.00, 75.20, 77.60, 81.40, 83.80, 87.95, 90.50, 95.60, 99.60, 103.30, 107.70, 110.60, 114.30, 117.30, 122.50},
                    {58.00, 62.00, 65.20, 67.60, 69.60, 71.20, 74.60, 77.30, 80.00, 83.60, 86.10, 90.40, 93.60, 98.10, 102.70, 106.50, 110.90, 114.70, 118.60, 120.70, 126.50},
                    {60.50, 64.00, 67.30, 69.50, 72.00, 74.00, 76.70, 79.40, 83.50, 86.90, 89.00, 93.50, 97.00, 101.90, 107.60, 110.40, 115.40, 119.20, 123.60, 125.50, 132.40},
            };
            return ret;
        } else {
            double[][] ret = {
                    {52.60, 56.30, 59.30, 61.20, 63.70, 64.90, 68.20, 70.70, 73.00, 76.50, 79.10, 82.30, 85.20, 89.90, 94.50, 97.30, 100.60, 103.60, 108.00, 110.75, 114.40},
                    {53.40, 57.15, 60.20, 62.50, 64.50, 65.80, 69.10, 71.80, 74.20, 77.80, 80.20, 83.90, 86.60, 91.20, 95.80, 99.50, 102.60, 105.60, 109.80, 112.70, 116.40},
                    {55.00, 58.70, 61.80, 64.10, 66.00, 67.80, 71.10, 74.00, 76.30, 80.20, 82.40, 86.10, 89.00, 94.10, 98.90, 102.60, 106.00, 109.50, 113.20, 116.50, 120.80},
                    {57.10, 60.50, 63.70, 66.00, 68.00, 69.50, 73.10, 75.90, 78.50, 82.50, 85.00, 88.40, 92.00, 96.75, 101.50, 105.70, 109.70, 112.70, 116.70, 120.50, 125.30},
                    {59.60, 62.60, 66.00, 68.00, 69.90, 72.10, 75.60, 78.30, 81.60, 85.70, 87.70, 92.50, 95.20, 100.00, 106.20, 110.40, 114.00, 117.30, 121.70, 125.00, 130.00}
            };
            return ret;
        }
    }

    public static String[] weightLineNames(String gender){
        String[] ret = {"P3", "P20", "P50", "P80", "P97"};
        return ret;
    }

    public static double[][] weightLineValues(String gender){
        if(gender.equals("男")) {
            double[][] ret = {
                    {3.98, 5.00, 5.95, 6.45, 6.82, 7.28, 7.68, 8.13, 8.53, 9.20, 9.80, 10.30, 11.00, 11.80, 12.50, 13.60, 14.70, 15.10, 15.96, 16.95, 18.21},
                    {4.40, 5.65, 6.50, 7.16, 7.60, 8.01, 8.60, 9.00, 9.40, 10.14, 10.60, 11.45, 12.00, 13.20, 14.18, 15.00, 16.10, 17.00, 18.05, 19.05, 20.66},
                    {4.90, 6.19, 7.12, 7.85, 8.25, 8.80, 9.34, 9.90, 10.29, 11.10, 11.45, 12.40, 13.10, 14.20, 15.50, 16.40, 17.75, 18.90, 20.06, 21.10, 23.50},
                    {5.45, 6.77, 7.75, 8.60, 9.05, 9.70, 10.02, 10.80, 11.30, 12.01, 12.55, 13.60, 14.30, 15.50, 17.00, 18.30, 19.82, 21.50, 23.15, 24.24, 27.45},
                    {6.30, 7.80, 8.60, 9.70, 10.40, 10.74, 11.36, 11.95, 12.80, 13.25, 13.85, 15.20, 16.35, 18.20, 20.15, 21.30, 23.90, 25.90, 29.00, 30.10, 33.70},
            };
            return ret;
        }else{
            double[][] ret = {
                    {3.66, 4.70, 5.40, 6.00, 6.31, 6.60, 7.24, 7.55, 8.20, 8.50, 9.05, 9.90, 10.30, 11.15, 12.20, 13.00, 13.85, 14.60, 15.70, 16.15, 17.23},
                    {3.95, 5.00, 5.78, 6.40, 6.70, 7.00, 7.70, 8.27, 8.55, 9.10, 9.65, 10.40, 10.95, 12.00, 13.05, 14.01, 14.70, 15.50, 16.60, 17.50, 18.62},
                    {4.11, 5.20, 6.00, 6.65, 6.99, 7.30, 8.00, 8.60, 8.87, 9.48, 10.00, 10.85, 11.40, 12.45, 13.60, 14.50, 15.40, 16.20, 17.50, 18.58, 19.40},
                    {4.56, 5.68, 6.55, 7.20, 7.60, 8.05, 8.80, 9.30, 9.75, 10.35, 10.82, 11.75, 12.50, 13.50, 14.90, 15.95, 17.03, 18.00, 19.30, 20.40, 22.00},
                    {5.10, 6.30, 7.24, 7.90, 8.38, 8.84, 9.63, 10.17, 10.70, 11.38, 11.88, 12.80, 13.70, 15.02, 16.37, 17.66, 18.95, 20.30, 21.70, 23.35, 25.30},
                    {5.81, 7.15, 8.00, 8.75, 9.50, 9.90, 10.62, 11.50, 12.05, 12.66, 13.35, 14.40, 15.91, 17.37, 19.08, 20.80, 23.00, 23.42, 25.10, 27.30, 30.10},
            };
            return ret;
        }
    }



    private static double [][] trimWithMonth(double[][] ret, int month){
        //boy_height_month = {0,1,2,3,4,5,6,7,8,9,10,11,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57,60,63,66,69,72,75,78,81},
        if (month < 36) {
            // trim to 21;
            for (int i = 0;i < ret.length; i++){
                double[] lineValue = ret[i];
                ret[i] = Arrays.copyOfRange(lineValue, 0, 22);
            }
        }else if (month >= 36 && month < 54){
            for (int i = 0;i < ret.length; i++){
                double[] lineValue = ret[i];
                ret[i] = Arrays.copyOfRange(lineValue, 19, 32);
            }
        }else{
            for (int i = 0;i < ret.length; i++){
                double[] lineValue = ret[i];
                ret[i] = Arrays.copyOfRange(lineValue, lineValue.length - 11, lineValue.length);
            }
        }

        return ret;
    }

    public static double[][] heightLineValuesV2(String gender, int month) {
        double[][] ret = null;
        String[] names = null;

        if (gender.equals("男")) {
            names = new String[]{"boy_height_N3SD", "boy_height_N2SD", "boy_height_N1SD", "boy_height_SD", "boy_height_P1SD", "boy_height_P2SD", "boy_height_P3SD"};
        }else{
            names = new String[]{"girl_height_N3SD", "girl_height_N2SD", "girl_height_N1SD", "girl_height_SD", "girl_height_P1SD", "girl_height_P2SD", "girl_height_P3SD"};
        }

        ret = new double[names.length][];
        for (int i = 0; i < names.length; i++){
            ret[i] = (double[]) StandardValues.valuesMap().get(names[i]);
        }

        ret = trimWithMonth(ret, month);

        return ret;
    }

    public static int[] monthValuesV2(String gender, int month) {
        int[] ret = (int[]) StandardValues.valuesMap().get("boy_height_month");  //always the same

        if (month < 36) {
            // trim to 21;
            ret = Arrays.copyOfRange(ret, 0, 22);
        }else if (month >= 36 && month < 54){
            ret = Arrays.copyOfRange(ret, 19, 32);
        }else{
            ret = Arrays.copyOfRange(ret, ret.length - 11, ret.length);
        }

        return ret;
    }

    public static String[] heightLineNamesV2(String gender){
        return SD_NAMES;
    }

    public static String[] weightLineNamesV2(String gender){
        return SD_NAMES;
    }

    public static double[][] weightLineValuesV2(String gender, int month){
        double[][] ret = null;
        String[] names = null;
        if(gender.equals("男")) {
            names = new String[]{"boy_weight_N3SD","boy_weight_N2SD", "boy_weight_N1SD", "boy_weight_SD", "boy_weight_P1SD", "boy_weight_P2SD", "boy_weight_P3SD"};
        }else{
            names = new String[] {"girl_weight_N3SD","girl_weight_N2SD", "girl_weight_N1SD", "girl_weight_SD", "girl_weight_P1SD", "girl_weight_P2SD", "girl_weight_P3SD"};
        }

        ret = new double[names.length][];
        for (int i = 0; i < names.length; i++){
            ret[i] = (double[]) StandardValues.valuesMap().get(names[i]);
        }

        ret = trimWithMonth(ret, month);

        return ret;
    }

//    public static void main(String[] args) {
//
//        String gender = "男";
//
//        encodedHeightChart(gender, 36, 100);
//
//        heightLineValuesV2("男", 30);
//        weightLineValuesV2("男", 37);
//
//        String name = "可可";
//
//        String weight = "25";
//        String height = "110";
//
//        Map<String,String > resultMap = new HashMap<>();
//        String g = "boy";
//        DateTime now = DateTime.now();
//        Date birthday = DateTime.parse("2017-04-08").toDate();
//        Interval interval = new Interval(birthday.getTime(),now.toDate().getTime());
//        int month = interval.toPeriod(PeriodType.months()).getMonths();
//
//        Double w = Double.parseDouble(weight);
//        String wResult = HealthReportUtil.calcHeightPercent(month,g,w);
//        resultMap.put("wResult",wResult);
//
//        Double h = Double.parseDouble(height);
//        String hResult = HealthReportUtil.calcHeightPercent(month,g,h);
//        resultMap.put("hResult",hResult);
//
//        System.out.println(resultMap);
//
//        {
//            String conclusion = heightConclusion(name, g, h, month);
//            System.out.println(conclusion);
//
//            conclusion = weightConclusion(name, g, h, w, month);
//            System.out.println(conclusion);
//        }
//        {
//            w = 20D;
//            String conclusion = heightConclusion(name, g, h, month);
//            System.out.println(conclusion);
//
//            conclusion = weightConclusion(name, g, h, w, month);
//            System.out.println(conclusion);
//        }
//        {
//            w = 15D;
//            String conclusion = heightConclusion(name, g, h, month);
//            System.out.println(conclusion);
//
//            conclusion = weightConclusion(name, g, h, w, month);
//            System.out.println(conclusion);
//        }
//        {
//            w = 13D;
//            String conclusion = heightConclusion(name, g, h, month);
//            System.out.println(conclusion);
//
//            conclusion = weightConclusion(name, g, h, w, month);
//            System.out.println(conclusion);
//        }
//        {
//            w = 10D;
//            h = 105D;
//            String conclusion = heightConclusion(name, g, h, month);
//            System.out.println(conclusion);
//
//            conclusion = weightConclusion(name, g, h, w, month);
//            System.out.println(conclusion);
//        }
//        {
//            w = 15D;
//            h = 100D;
//            String conclusion = heightConclusion(name, g, h, month);
//            System.out.println(conclusion);
//
//            conclusion = weightConclusion(name, g, h, w, month);
//            System.out.println(conclusion);
//        }
//        {
//            w = 15D;
//            h = 95D;
//            String conclusion = heightConclusion(name, g, h, month);
//            System.out.println(conclusion);
//
//            conclusion = weightConclusion(name, g, h, w, month);
//            System.out.println(conclusion);
//        }
//    }


    public static Map bmiInfo( String name, Integer gender, String birthday, String weight, String height ) {

        Map result = new HashMap();

        String genderStr = (gender == 0) ? "女" : "男";
        float actualMonth = 0;

        if (StringUtils.isNotBlank(birthday)) {

            actualMonth = (float) DateUtil.betweenMonthsV2(new Date(), DateUtil.parseToDate(birthday));
            result.put("actualMonth", actualMonth);

            double h = 0;
            double w = 0;
            if (StringUtils.isNotBlank(weight) && StringUtils.isNotBlank(height)) {
                h = Double.parseDouble(height);
                w = Double.parseDouble(weight);

                String heightConclusion = HealthReportUtil.heightConclusion(name, genderStr, h, (int) actualMonth);
                String weightConclusion = HealthReportUtil.weightConclusion(name, genderStr, h, w, (int) actualMonth);
                result.put("heightConclusion", heightConclusion);
                result.put("weightConclusion", weightConclusion);
            }
        }

        result.put("heightLineNames", HealthReportUtil.heightLineNames(genderStr));
        result.put("heightLineValues",  HealthReportUtil.heightLineValues(genderStr));
        result.put("weightLineNames", HealthReportUtil.weightLineNames(genderStr));
        result.put("weightLineValues", HealthReportUtil.weightLineValues(genderStr));

        result.put("monthBaseValuesV2", HealthReportUtil.monthValuesV2(genderStr, (int) actualMonth));
        result.put("heightLineNamesV2", HealthReportUtil.heightLineNamesV2(genderStr));
        result.put("heightLineValuesV2",  HealthReportUtil.heightLineValuesV2(genderStr, (int) actualMonth));
        result.put("weightLineNamesV2", HealthReportUtil.weightLineNamesV2(genderStr));
        result.put("weightLineValuesV2", HealthReportUtil.weightLineValuesV2(genderStr, (int) actualMonth));

        return result;
    }
}
